#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/serial.h>

/* baudrate settings are defined in <asm/termbits.h>, which is
included by <termios.h> */
#define BAUDRATE B115200            
/* change this definition for the correct port */
#define MODEMDEVICE1 "/dev/ttySU2"
#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define FALSE 0
#define TRUE 1

#define TIOCGRS485 0x542E 
#define TIOCSRS485 0x542F

volatile int STOP = FALSE; 

int main(int argc, char *argv[])
{
    int fd, c, res, i, has_parity;
    struct termios oldtio,newtio;
    char buf[512], id, parityT;
    char MODEMDEVICE[16] = {"/dev/ttyO"};
    struct serial_rs485     rs485;          /* rs485 settings */
    
    if (argc != 3 )
    {
        printf("\n\rusage: %s uart[2] [n|o|e]\n\r", argv[0]);
        exit(-1);
    }
    
    id = argv[1][0];
    MODEMDEVICE[9] = id;
    MODEMDEVICE[10] = 0;    
    
    switch(id)
    {
        case '1':
            break;

        case '2':
            break;

        case '3':
            break;

        case '4':
            break;

        case '5':
            break;

        case '0':
        default:
            printf("\n\rerror:unsupported uart %s\n\r", id);
            exit(-1);
            break;
    }
    
    printf("\n\rtry to open %s for uart test\n\r", MODEMDEVICE);
    
    /* 
      Open modem device for reading and writing and not as controlling tty
      because we don't want to get killed if linenoise sends CTRL-C.
    */
    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); 
    if (fd <0) {perror(MODEMDEVICE); exit(-1); }

    tcgetattr(fd,&oldtio); /* save current serial port settings */
    bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

    newtio.c_cflag |= (CLOCAL | CREAD);
    newtio.c_cflag |= BAUDRATE;
    newtio.c_cflag &= ~CSTOPB;
    newtio.c_cflag &= ~PARENB;
    newtio.c_cflag &= ~CSIZE;
    newtio.c_cflag |= CS8;
    newtio.c_cflag &= ~CRTSCTS;
    /* turn off icrnl and ixon to send out 0x0d, 0x11, 0x13 */
    newtio.c_iflag &= ~(ICRNL | IXON);

    //parity
    parityT = argv[2][0];
    has_parity = 0;
    switch(parityT)
    {
        case 'o':
            newtio.c_cflag |= (PARODD | PARENB);
            printf("use odd parity for tx\n\r");
            has_parity = 1;
            break;

        case 'e':
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
            printf("use even parity for tx\n\r");
            has_parity = 2;
            break;

        case 'n':
        default:
            printf("use NO parity for tx\n\r");
            break;
    }

    newtio.c_lflag = 0;
    newtio.c_oflag = 0;

    newtio.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
    newtio.c_oflag  &= ~OPOST;   /*Output*/

    newtio.c_cc[VMIN] =  1; 
    newtio.c_cc[VTIME] = 0;

    newtio.c_iflag &= ~(IXON|IXOFF|IXANY);
    // enable rx parity check
    if (has_parity)
    {
        newtio.c_iflag |= (INPCK | ISTRIP);
        printf("check RX parity for %s\n\r", (has_parity == 1) ? "ODD" : "EVEN");
    }

    cfsetispeed(&newtio, BAUDRATE);
    cfsetospeed(&newtio, BAUDRATE);
 
    /* 
      now clean the modem line and activate the settings for the port
    */
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd,TCSANOW,&newtio);

    if (id == '3')
    {
        printf("\n\renable RS485 mode on USART3\n\r");
        memset((void *)&rs485, 0, sizeof(struct serial_rs485));
        rs485.flags = SER_RS485_RTS_BEFORE_SEND | SER_RS485_ENABLED;
        rs485.delay_rts_before_send = 10; /* Milliseconds */ 
        ioctl(fd, TIOCSRS485, (void *)&rs485);    
    }

    printf("%s opened for USART%c, fd = %d\n\r", MODEMDEVICE, id, fd);
    printf("Please move the USART cable to %s within 10 seconds\n\r", MODEMDEVICE);

    for (i = 0; i < 10; i ++)
    {
        memset(buf, 0, 256);
        sprintf(buf, "test will start in %d seconds\n\r", 10 - i);
        write(fd, buf, 256);
        sleep(1);
    }

    memset(buf, 0, 256);
    sprintf(buf, "\n\rmcuzone ttySU test code for MDK1808(RMII version) on %s\n\rreceive a z will exit test\n\r", MODEMDEVICE);
     
     /* test ttyS TX */
    write(fd, buf, 256);

    printf("try to read from %s\n\r", MODEMDEVICE);
    
    /*
      terminal settings done, now handle input
      In this example, inputting a 'z' at the beginning of a line will 
      exit the program.
    */
    while (STOP == FALSE) {     /* loop until we have a terminating condition */
    /* read blocks program execution until a line terminating character is 
        input, even if more than 255 chars are input. If the number
        of characters read is smaller than the number of chars available,
        subsequent reads will return the remaining chars. res will be set
        to the actual number of characters actually read */
//        memset(buf, 0, 256);
//        sleep(1000); // 1 second
        res = read(fd,buf,256); 
        buf[res]=0;             /* set end of string, so we can printf */
        printf(":%s:%d\n", buf, res);
        write(fd, buf, res);
        if (buf[0] == 'z') STOP = TRUE;
    }
    
    /* restore the old port settings */
    tcsetattr(fd,TCSANOW,&oldtio);
    close(fd);
}
